/*
dead6800 version 1.00

started janurary 2008

copyright 2008 Dead_Body
*/

#include "StdAfx.h"
#include "Dead6800.h"

#define F_CARRY		0x01
#define F_OVERFLOW	0x02
#define F_ZERO			0x04
#define F_NEGATIVE	0x08
#define F_IRQ			0x10
#define F_HALFCARRY	0x20

#define A acc.b.A
#define B acc.b.B
#define D acc.D

static int INT_NMI = 1;
static int INT_IRQ = 2;

static unsigned char c6800Cycles[256] =
{
0,2,0,0,0,0,2,2,4,4,2,2,2,2,2,2,//2,2,
2,2,0,0,0,0,2,2,0,2,0,2,0,0,0,0,
4,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,0,5,0,10,0,0,9,12,
2,0,0,2,2,0,2,2,2,2,2,0,2,2,0,2,
2,0,0,2,2,0,2,2,2,2,2,0,2,2,0,2,
7,0,0,7,7,0,7,7,7,7,7,0,7,7,4,7,
6,0,0,6,6,0,6,6,6,6,6,0,6,6,3,6,
2,2,2,0,2,2,2,0,2,2,2,2,3,8,3,0,
3,3,3,0,3,3,3,4,3,3,3,3,4,0,4,5,
5,5,5,0,5,5,5,6,5,5,5,5,6,8,6,7,
4,4,4,0,4,4,4,5,4,4,4,4,5,9,5,6,
2,2,2,0,2,2,2,0,2,2,2,2,0,0,3,0,
3,3,3,0,3,3,3,4,3,3,3,3,0,0,4,5,
5,5,5,0,5,5,5,6,5,5,5,5,0,0,6,7,
4,4,4,0,4,4,4,5,4,4,4,4,0,0,5,6
};

static char *OpcodeTable[256] =
{
"??  ","NOP ","??  ","??  ","??  ","??  ","TAP ","TPA ","INX ","DEX ","CLV ","SEV ","CLC ","SEC ","CLI ","SEI ",
"SBA ","CBA ","??  ","??  ","??  ","??  ","TAB ","TBA ","??  ","DAA ","??  ","ABA ","??  ","??  ","??  ","??  ",
"BRA ","BRN ","BHI ","BLS ","BCC ","BLO ","BNE ","BEQ ","BVC ","BVS ","BPL ","BMI ","BGE ","BLT ","BGT ","BLE ",
"TSX ","INS ","PULA","PULB","DES ","TXS ","PSHA","PSHB","??  ","RTS ","??  ","RTI ","??  ","??  ","WAI ","SWI ",
"NEGA","??  ","??  ","COMA","LSRA","??  ","RORA","ASRA","ASLA","ROLA","DECA","??  ","INCA","TSTA","??  ","CLRA",
"NEGB","??  ","??  ","COMB","LSRB","??  ","RORB","ASRB","LSLB","ROLB","DECB","??  ","INCB","TSTB","??  ","CLRB",
"NEG ","??  ","??  ","COM ","LSR ","??  ","ROR ","ASR ","ASL ","ROL ","DEC ","??  ","INC ","TST ","JMP ","CLR ",
"NEG ","??  ","??  ","COM ","LSR ","??  ","ROR ","ASR ","ASL ","ROL ","DEC ","??  ","INC ","TST ","JMP ","CLR ",
"SUBA","CMPA","SBCA","SUBD","ANDA","BITA","LDA ","??  ","EORA","ADCA","ORA ","ADDA","CMPX","BSR ","LDS ","STS ",
"SUBA","CMPA","SBCA","SUBD","ANDA","BITA","LDA ","STA ","EORA","ADCA","ORA ","ADDA","CMPX","JSR ","LDS ","STS ",
"SUBA","CMPA","SBCA","SUBD","ANDA","BITA","LDA ","STA ","EORA","ADCA","ORA ","ADDA","CMPX","JSR ","LDS ","STS ",
"SUBA","CMPA","SBCA","SUBD","ANDA","BITA","LDA ","STA ","EORA","ADCA","ORA ","ADDA","CMPX","JSR ","LDS ","STS ",
"SUBB","CMPB","SBCB","ADDD","ANDB","BITB","LDB ","??  ","EORB","ADCB","ORB ","ADDB","??  ","??  ","LDX ","STX ",
"SUBB","CMPB","SBCB","ADDD","ANDB","BITB","LDB ","STB ","EORB","ADCB","ORB ","ADDB","??  ","??  ","LDX ","STX ",
"SUBB","CMPB","SBCB","ADDD","ANDB","BITB","LDB ","STB ","EORB","ADCB","ORB ","ADDB","??  ","??  ","LDX ","STX ",
"SUBB","CMPB","SBCB","ADDD","ANDB","BITB","LDB ","STB ","EORB","ADCB","ORB ","ADDB","??  ","??  ","LDX ","STX "
};

CDead6800::CDead6800()
{
Debug = 2;
CC = 0;
}

CDead6800::~CDead6800()
{
}

void CDead6800::Init()
{
int i;

for(i=0;i<32;i++)
	{
	OpPages[i] =
	ReadPages[i] =
	WritePages[i] = 0;
	}
}

void CDead6800::Reset()
{
PC = ReadWord(0xFFFE);
D = 0;
X = 0;
S = 0;
CC |= F_IRQ;
Cycle = 0;
Interrupts = 0;
WaitInterrupt = 0;
}

void CDead6800::IRQ()
{
Interrupts |= INT_IRQ;
}

void CDead6800::NMI()
{
Interrupts |= INT_NMI;
}

#include "Dead6800funcs.cpp"				//class members not found here
#include "Dead6800ops.cpp"					//opcodes used in Step()

void CDead6800::Step()
{
u8 Opcode;										//current opcode
u8 tmp,ntmp,otmp;								//temp variables used by opcodes
u32 tmp2,tmp3;

CHECK_INTERRUPTS;
if(WaitInterrupt)
	{
	AddCycles(1);
	return;
	}
SavePC = PC;
Opcode = OpRead(PC++);
switch(Opcode)
	{
	#include "Dead6800switch.cpp"
	}
AddCycles(c6800Cycles[Opcode]);
//debug crap
if(Debug == 3)
	{
	char dis[128];

	Disassemble(dis,SavePC);
	message("$%04X: %s D=$%04X X=$%04X S=$%04X CC=$%02X\n",SavePC,dis,D,X,S,CC);
	}
}

u32 CDead6800::Execute(u32 c)
{
u32 c1 = Cycle + c;
u32 c2 = Cycle;

while(Cycle < c1)
	Step();
return(Cycle - c2);
}

void CDead6800::SaveState(CState *s)
{
s->Write16(PC);
s->Write16(D);
s->Write16(X);
s->Write16(S);
s->Write8(CC);
s->Write32(EA);
s->Write32(Cycle);
s->Write32(Interrupts);
s->Write32(WaitInterrupt);
s->Write16(SavePC);
}

void CDead6800::LoadState(CState *s)
{
PC = s->Read16();
D = s->Read16();
X = s->Read16();
S = s->Read16();
CC = s->Read8();
EA = s->Read32();
Cycle = s->Read32();
Interrupts = s->Read32();
WaitInterrupt = s->Read32();
SavePC = s->Read16();
}

#define ADDR_SHIFT	11
#define ADDR_MASK		0x7FF

u8 CDead6800::OpRead(u32 addr)
{
if(OpPages[addr >> ADDR_SHIFT])
	return(OpPages[addr >> ADDR_SHIFT][addr & ADDR_MASK]);
return(OpPtr(User,addr));
}

u8 CDead6800::Read(u32 addr)
{
if(addr >= 0x10000)
	{
	message("read invalid memory address '$%X', cycle %ld, PC = $%04X\n",addr,Cycle,PC);
	addr &= 0xFFFF;
	}
if(ReadPages[addr >> ADDR_SHIFT])
	return(ReadPages[addr >> ADDR_SHIFT][addr & ADDR_MASK]);
return(ReadPtr(User,addr));
}

void CDead6800::Write(u32 addr,u8 data)
{
if(addr >= 0x10000)
	{
	message("write invalid memory address '$%X', cycle %ld, PC = $%04X\n",addr,Cycle,PC);
	addr &= 0xFFFF;
	}
if(WritePages[addr >> ADDR_SHIFT])
	WritePages[addr >> ADDR_SHIFT][addr & ADDR_MASK] = data;
WritePtr(User,addr,data);
}

u16 CDead6800::ReadWord(u32 addr)
{
u16 ret = 0;

ret |= Read(addr + 0) << 8;
ret |= Read(addr + 1) << 0;
return(ret);
}

void CDead6800::WriteWord(u32 addr,u16 data)
{
Write(addr + 0,(u8)(data >> 8) & 0xFF);
Write(addr + 1,(u8)(data >> 0) & 0xFF);
}

void CDead6800::SetUser(void *u)
{
User = u;
}

void CDead6800::SetPage(int p,u8 *d)
{
OpPages[p] = d;
ReadPages[p] = d;
WritePages[p] = d;
}

void CDead6800::SetOpPage(int p,u8 *d)
{
OpPages[p] = d;
}

void CDead6800::SetReadPage(int p,u8 *d)
{
ReadPages[p] = d;
}

void CDead6800::SetWritePage(int p,u8 *d)
{
WritePages[p] = d;
}

void CDead6800::SetOp(ReadFunc *r)
{
OpPtr = r;
}

void CDead6800::SetRead(ReadFunc *r)
{
ReadPtr = r;
}

void CDead6800::SetWrite(WriteFunc *w)
{
WritePtr = w;
}

//debugger stuff
enum c6800addrmodes_e
	{
	c6800_addr_none = 0,
	c6800_addr_implied,
	c6800_addr_immediate,
	c6800_addr_immediate8,
	c6800_addr_direct,
	c6800_addr_indexed,
	c6800_addr_extended,
	c6800_addr_relative,
	c6800_addr_relative16
	};

static int GetAddrMode(u8 Opcode)
{
switch(Opcode & 0xF0)
	{
	case 0x00:
	case 0x10:
	case 0x30:
	case 0x40:
	case 0x50:
		return(c6800_addr_implied);
	case 0x20:
		return(c6800_addr_relative);
		return(c6800_addr_implied);
	case 0x60:
		return(c6800_addr_indexed);
	case 0x70:
		return(c6800_addr_extended);
	case 0x80:
		if(Opcode == 0x8D)		//bsr, relative
			return(c6800_addr_relative);
	case 0xC0:
		return(c6800_addr_immediate);
	case 0x90:
	case 0xD0:
		return(c6800_addr_direct);
	case 0xA0:
	case 0xE0:
		return(c6800_addr_indexed);
	case 0xB0:
	case 0xF0:
		return(c6800_addr_extended);
	}
return(c6800_addr_none);
}

u32 CDead6800::Disassemble(char *dest,u32 p)
{
u8 Opcode,N,Reg;
char *Op;
char RegList[] = "XYUS";

Opcode = OpRead(p++);
Op = OpcodeTable[Opcode];
switch(GetAddrMode(Opcode))
	{
	case c6800_addr_none:
	case c6800_addr_implied:
		sprintf(dest,"($%02X)\t%s",Opcode,Op);
		break;
	case c6800_addr_immediate:
		sprintf(dest,"($%02X)\t%s\t$%04X",Opcode,Op,ReadWord(p));
		p += 2;
		break;
	case c6800_addr_immediate8:
		sprintf(dest,"($%02X)\t%s\t$%02X",Opcode,Op,Read(p));
		p += 1;
		break;
	case c6800_addr_direct:
		sprintf(dest,"($%02X)\t%s\t<$%02X ($%04X)",Opcode,Op,Read(p),Read(p));
		p += 1;
		break;
	case c6800_addr_indexed:
		N = Read(p++);
		Reg = RegList[(N >> 5) & 3];
		if((N & 0x80) == 0)
			{
			if((N & 0x10) == 0)
				sprintf(dest,"($%02X)\t%s\t$%X,%c",Opcode,Op,N & 0xF,Reg);
			else
				sprintf(dest,"($%02X)\t%s\t-$%X,%c",Opcode,Op,N & 0xF,Reg);
			}
		else
			{
			char FF[2] = "";
			char FB[2] = "";
			
			if(N & 0x10)
				{
				strcpy(FF,"[");
				strcpy(FB,"]");
				}
			switch(N & 0xF)
				{
				case 0:	sprintf(dest,"($%02X)\t%s\t%s,%c+%s",Opcode,Op,FF,Reg,FB);	break;
				case 1:	sprintf(dest,"($%02X)\t%s\t%s,%c++%s",Opcode,Op,FF,Reg,FB);	break;
				case 2:	sprintf(dest,"($%02X)\t%s\t%s,-%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 3:	sprintf(dest,"($%02X)\t%s\t%s,--%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 4:	sprintf(dest,"($%02X)\t%s\t%s,%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 5:	sprintf(dest,"($%02X)\t%s\t%sB,%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 6:	sprintf(dest,"($%02X)\t%s\t%sA,%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 8:	sprintf(dest,"($%02X)\t%s\t%s<$%02X,%c%s",Opcode,Op,FF,Read(p++),Reg,FB); break;
				case 9:	sprintf(dest,"($%02X)\t%s\t%s>$%04X,%c%s",Opcode,Op,FF,ReadWord(p),Reg,FB); p += 2; break;
				case 11:	sprintf(dest,"($%02X)\t%s\t%sD,%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 12:	sprintf(dest,"($%02X)\t%s\t%s<$%02X,PC%s",Opcode,Op,FF,Read(p++),FB); break;
				case 13:	sprintf(dest,"($%02X)\t%s\t%s>$%04X,PC%s",Opcode,Op,FF,ReadWord(p),FB); p += 2; break;
				case 15:	sprintf(dest,"($%02X)\t%s\t%s$%04X%s",Opcode,Op,FF,ReadWord(p),FB); p += 2; break;
				}
			}
		break;
	case c6800_addr_extended:
		sprintf(dest,"($%02X)\t%s\t[$%02X]",Opcode,Op,ReadWord(p));
		p += 2;
		break;
	case c6800_addr_relative:
		sprintf(dest,"($%02X)\t%s\t$%04X",Opcode,Op,p + (s8)(Read(p) + 1));
		p += 1;
		break;
	case c6800_addr_relative16:
		sprintf(dest,"($%02X)\t%s\t$%04X",Opcode,Op,p + (s16)(ReadWord(p) + 1));
		p += 2;
		break;
	}
return(p);
}

char *CDead6800::GenerateRegNames(char *dest)
{
sprintf(dest,
"pc:\n"
"d:\n"
"x:\n"
"s:\n"
"cc:\n"
"\n"
"vectors\n"
"irq:\n"
"swi:\n"
"nmi:\n"
"reset:\n"
);
return(dest);
}

#undef D

char *CDead6800::GenerateRegData(char *dest)
{
sprintf(dest,
"$%04X\n"
"$%04X\n"
"$%04X\n"
"$%04X\n"
"$%02X\n"
"\n"
"\n"
"$%04X\n"
"$%04X\n"
"$%04X\n"
"$%04X\n",
	PC,acc.D,X,S,CC,
	ReadWord(0xFFF8),ReadWord(0xFFFA),ReadWord(0xFFFC),ReadWord(0xFFFE)
);
return(dest);
}

u32 CDead6800::GetPC()
{
return(PC);
}

u32 CDead6800::GetCycle()
{
return(Cycle);
}
